package com.kevin.epacms.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kevin.epacms.constant.CommonConstant;
import com.kevin.epacms.entity.News;
import com.kevin.epacms.entity.vo.ConditionVo;
import com.kevin.epacms.mapper.NewsMapper;
import com.kevin.epacms.service.NewsService;
import com.kevin.epacms.util.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author kevin
 * @since 2022-11-10
 */
@Service
public class NewsServiceImpl extends ServiceImpl<NewsMapper, News> implements NewsService {
    private static final String HTTP_REX = "^((http|https)://)([\\w-]+\\.)+[\\w-]+";
    private static final Pattern HTTP_PATTERN = Pattern.compile(HTTP_REX);
    @Resource
    private RedisUtil redisUtil;

    @Override
    public Result getNewsByPage(Integer currentPage, Integer pageSize, List<ConditionVo> conditions) {
        QueryWrapper<News> queryWrapper = SearchUtil.parseWhereSql(conditions);
        queryWrapper.orderByDesc("create_time");
        Page<News> page = baseMapper.selectPage(new Page<>(currentPage, pageSize), queryWrapper);
        PageList pageList = new PageList(page);
        return Result.ok(pageList);
    }

    @Override
    public Result deleteBatch(String idsStr) {
        List<Long> ids = Arrays.stream(idsStr.split(StrUtil.COMMA)).map(Long::parseLong).collect(Collectors.toList());
        return removeBatchByIds(ids) ? Result.ok(CommonConstant.SUCCESS) : Result.fail(CommonConstant.FAIL);
    }

    @Override
    public Result addNews(News news) {
        news.setCreateTime(LocalDateTime.now());
        return save(news) ? Result.ok(CommonConstant.SUCCESS) : Result.fail(CommonConstant.FAIL);
    }

    @Override
    public Result addBatchNews(List<News> newsList) {
        newsList.forEach(news -> news.setCreateTime(LocalDateTime.now()));
        return saveBatch(newsList) ? Result.ok(CommonConstant.SUCCESS) : Result.fail(CommonConstant.FAIL);
    }

    @Override
    public Result deleteNewsById(Long id) {
        return removeById(id) ? Result.ok(CommonConstant.SUCCESS) : Result.fail(CommonConstant.FAIL);
    }

    @Override
    public Result crawlNews(Integer currentPage, Integer pageSize, String url, String cssQuery) {
        Map<String, Object> map = new HashMap<>(3);
        String key = CommonConstant.NEWS_PREFIX + getHostName(url) + ":" + cssQuery;
        String newsJson = redisUtil.get(key);
        if (StrUtil.isNotBlank(newsJson)) {
            List<JSONObject> newsList = JSONUtil.toList(newsJson, JSONObject.class);
            Object[] newsData = newsList.stream()
                    .skip((long) (currentPage - 1) * pageSize)
                    .limit(pageSize).toArray();
            map.put("total", newsList.size());
            map.put("data", newsData);
            return Result.ok(map);
        }
        String[] strings = this.spiltURL(url);
        // 基本URL
        String baseUrl = strings[0];
        // 资源路径
        String pathSrc = strings[1];
        // 存储爬取到的新闻
        JSONArray newsList = new JSONArray();
        // 与目标URL建立连接，获取页面
        String content = HttpUtil.doGetHtml(baseUrl + pathSrc);
        // 解析页面
        Document document = Jsoup.parse(content);
        Elements elements = document.select(cssQuery + " a[href]");
        for (Element element : elements) {
            // 获取链接(可能是相对路径)
            String link = element.attr("href");
            JSONObject newsObj = new JSONObject();
            newsObj.set("title", element.text());
            newsObj.set("url", baseUrl + link);
            newsList.add(newsObj);
        }
        redisUtil.set(key, newsList, 30L, TimeUnit.MINUTES);
        Object[] newsData = newsList.stream()
                .skip((long) (currentPage - 1) * pageSize)
                .limit(pageSize).toArray();
        map.put("total", newsList.size());
        map.put("data", newsData);
        return Result.ok(map);
    }

    /**
     * 获得主机名部分
     *
     * @param url url
     * @return {@link String}
     */
    private String getHostName(String url) {
        String sub = url.substring(url.indexOf("//") + 2);
        return sub.substring(0, sub.indexOf("/"));
    }

    /**
     * 拆分URL
     *
     * @param url url
     * @return {@link String[]}
     */
    private String[] spiltURL(String url) {
        Matcher matcher = HTTP_PATTERN.matcher(url);
        String baseUrl = "";
        while (matcher.find()) {
            baseUrl = matcher.group(0);
        }
        String pathSrc = url.substring(baseUrl.length());
        return new String[]{baseUrl, pathSrc};
    }

}
